﻿#include "ZTimerHW.h"

#include <stdexcept>

#include "xparameters.h"				//包含器件的参数信息
#include "xscutimer.h"					//定时器中断的函数声明
#include "xscugic.h"					//包含中断的函数声明
#include "xil_printf.h"

#define TIMER_DEVICE_ID     XPAR_XSCUTIMER_0_DEVICE_ID   //定时器ID
#define INTC_DEVICE_ID      XPAR_SCUGIC_SINGLE_DEVICE_ID //中断ID
#define TIMER_IRPT_INTR     XPAR_SCUTIMER_INTR           //定时器中断ID
#define MIO_LED             0                            //led连接到 MIO0

ZTimerHW::ZTimerHW(int period, unsigned cpuFreq)
  : period_(period),
    // 私有定时器的时钟频率 = CPU时钟频率/2
    cpuFreq_(cpuFreq / 2) {
  this->initialize();
}

void ZTimerHW::add(Task task) {
  TaskInternal taskInternal{};
  taskInternal.name = task.name;
  taskInternal.callback = task.callback;
  taskInternal.period = task.period;
  callbacks_[task.name] = taskInternal;
}

void ZTimerHW::remove(const std::string &taskName) {
  callbacks_.erase(taskName);
}

void ZTimerHW::initialize() {
  initTimer();
  initInterrupt();
  // 启动定时器
  XScuTimer_Start(&timer_);
}

void ZTimerHW::initTimer() {
  //私有定时器初始化
  auto timer_cfg_ptr = XScuTimer_LookupConfig(TIMER_DEVICE_ID);
  if (!timer_cfg_ptr) {
    throw std::runtime_error("Failed to find timer configuration.");
  }
  auto status = XScuTimer_CfgInitialize(&timer_, timer_cfg_ptr,
                                        timer_cfg_ptr->BaseAddr);
  if (status != XST_SUCCESS) {
    throw std::runtime_error("Failed to initialize timer.");
  }

  // 计算定时器装载值
  const auto timerLoadValue{cpuFreq_ / 1000 * period_};
  // 加载计数周期
  XScuTimer_LoadTimer(&timer_, timerLoadValue);
  // 设置自动装载模式
  XScuTimer_EnableAutoReload(&timer_);
}

void ZTimerHW::initInterrupt() {
  //初始化中断控制器
  auto intc_cfg_ptr = XScuGic_LookupConfig(XPAR_SCUGIC_SINGLE_DEVICE_ID);
  XScuGic_CfgInitialize(&intc_, intc_cfg_ptr, intc_cfg_ptr->CpuBaseAddress);
  //设置并打开中断异常处理功能
  Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
                               (Xil_ExceptionHandler) XScuGic_InterruptHandler,
                               &intc_);
  Xil_ExceptionEnable();

  //设置定时器中断
  XScuGic_Connect(&intc_,TIMER_IRPT_INTR,
                  (Xil_ExceptionHandler) baseCallbackFunc,
                  this);

  XScuGic_Enable(&intc_,TIMER_IRPT_INTR);//使能GIC中的定时器中断
  XScuTimer_EnableInterrupt(&timer_);//使能定时器中断
}

void ZTimerHW::baseCallbackFunc(void *that) {
  // 指针还原
  auto *timer = static_cast<ZTimerHW *>(that);
  // 遍历当前的回调函数,如果过去的时间大于等于周期,则执行回调函数
  for (auto &callback : timer->callbacks_) {
    // 获得任务
    auto &task = callback.second;
    if (task.elapsedTime >= task.period) {
      if (task.callback) {
        // 执行回调函数
        task.callback();
      }
      // 重置过去的时间
      task.elapsedTime = 0;
    } else {
      // 增加过去的时间
      task.elapsedTime += timer->period_;
    }
  }
}